﻿/*
    Project: IV:MP GUI Editor (http://code.google.com/p/ivmp-gui-editor)
    Code license: GNU Lesser GPL (http://www.gnu.org/copyleft/lesser.html) 
 */
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace IvmpEditor
{
    public abstract class GUIElement
    {
        // Core description
        public const string TYPE = null;
        public abstract string GetTypeName();
        public abstract string GetDefaultVarName();
        public abstract Style GetBaseStyle();
        protected string variableName;
        protected FrameworkElement nativeControl;
        public virtual FrameworkElement NativeControl { get { return this.nativeControl; } }
        protected Dictionary<string, object> properties;
        protected GUIWindow parentWindow;
        protected WpfMoveControlAgent moveControlAgent;

        // Control:
        protected abstract void MakeControl();
        public WpfMoveControlAgent MoveControlAgent { get { return moveControlAgent; } }
        public virtual void InstallMoveControlAgent()
        {
            // Can be overridden if MoveControlAgent installation is unusual..
            // This should be called by some external code, not inside GUIElement logics to activate moving element by mouse
            moveControlAgent.InstallTo(new FrameworkElement[] { nativeControl });
        }
        public abstract string GetSQCode(ref SquirrelCodeStack sq);
        public virtual void SyncFromNative()
        {
            // Should be overridden, but base function is needed before any actions.
            PropertySet("Size", new Point(this.NativeControl.Width, this.NativeControl.Height));
            PropertySet("Position", new Point(this.NativeControl.Margin.Left, this.NativeControl.Margin.Top));
            PropertySet("Visible", (NativeControl.Visibility == Visibility.Visible));
        }
        public virtual void SyncToNative()
        {
            // Should be overridden, but base function is needed before any actions.
            // FIXME we cant link to WpfMoveControl, fixme
            WpfMoveControlAgent.SetControlPos(nativeControl, GetPosition());
            WpfMoveControlAgent.SetControlSize(nativeControl, GetSize());

            // Stylizing from properties:
            Stylize();
        }
        public virtual void Stylize()
        {
            var style = CEGUI.CopyStyle(GetBaseStyle());
            foreach(var iter in properties)
                if (iter.Value is CEGUI.GUIProperty)
                {
                    var property = (CEGUI.GUIProperty)iter.Value;
                    CEGUI.ApplyProperty(style, property);
                }
            NativeControl.Style = style;
        }
        
        // Properties:
        public string GetName() { return variableName; }
        public void SetName(string newName)
        {
            variableName = newName;
            // We also set native x:Name:
            if (nativeControl != null)
                nativeControl.Name = variableName;
        }
        public GUIWindow GetParent() { return parentWindow; }
        public void SetParent(GUIWindow wnd)
        {
            if (wnd == null)
            {
                if (parentWindow != null)
                {
                    var oldParent = parentWindow;
                    parentWindow = null;
                    oldParent.ChildRemove(this);
                }
                return;
            }
            else if (parentWindow != null)
                SetParent(null);

            parentWindow = wnd;
            wnd.ChildAdd(this);
        }
        public Point GetSize() { return (Point)PropertyGet("Size"); }
        public void SetSize(Point newSz, bool native)
        {
            PropertySet("Size", newSz);
            if (native && nativeControl != null)
            {
                nativeControl.Width = newSz.X;
                nativeControl.Height = newSz.Y;
            }
        }
        public Point GetPosition() { return (Point)PropertyGet("Position"); }
        public void SetPosition(Point newPos, bool native)
        {
            PropertySet("Position", newPos);
            if (native && nativeControl != null)
            {
                nativeControl.Margin = new Thickness(
                    newPos.X, newPos.Y,
                    nativeControl.Margin.Right, nativeControl.Margin.Bottom);
            }
        }
        public string GetText() { return PropertyGet("Text") as string; }
        public virtual void SetText(string newTxt, bool native)
        {
            PropertySet("Text", newTxt);
            // USE: SetText must be overridden, but base method called before-
            // If <native>==true, we should change text on native control.
        }
        public void SetText(string newTxt) { SetText(newTxt, nativeControl != null); }
        public void SetVisible(bool visible, bool native)
        {
            PropertySet("Visible", visible);
            if (native && nativeControl != null)
            {
                if (visible) nativeControl.Visibility = Visibility.Visible;
                else nativeControl.Visibility = Visibility.Hidden;
            }
        }
        public bool GetVisible(bool visible, bool native) { return (bool)PropertyGet("Visible", true); }

        // GUI element properties stuff:
        protected bool PropertyIsSet(string key)
        {
            // Check property is set(exists):
            return properties.ContainsKey(key);
        }
        protected void PropertyUnset(string key)
        {
            // Remove property:
            properties.Remove(key);
        }
        protected void PropertySet(string key, object value)
        {
            // Adds property of changes value.
            if (!this.PropertyIsSet(key))
                properties.Add(key, value);
            else properties[key] = value;
        }
        protected object PropertyGet(string key, object defaultValue)
        {
            if (!PropertyIsSet(key))
                return defaultValue;
            else return properties[key];
        }
        protected object PropertyGet(string key) { return PropertyGet(key, null); }
        public void PropertySet(CEGUI.GUIProperty property)
        {
        check_e:
            foreach (var iter in properties)
                if (iter.Value == property.Name)
                {
                    // Remove property with same name
                    properties.Remove(iter.Key);
                    goto check_e;
                }

            PropertySet(property.Name, property);
        }

        // Statics & global.
        public static string PointToString(Point pt) { return pt.ToString().Replace(';', ','); }
        public static string strDoubleCorrect(double d) { return strDoubleCorrect(d, false); }
        public static string strDoubleCorrect(double d, bool allowLowerZero)
        {
            if (!allowLowerZero && d < 0) d = 0;
            string s = d.ToString();
            if (!s.Contains("."))
                s += ".0";
            return s;
        }
        public static System.Windows.Media.FontFamily GetFontFromFile(string fontStr)
        {
            string fontDirectory = System.IO.Path.GetFullPath(@".\");
            var uri = new Uri(fontDirectory);
            return new System.Windows.Media.FontFamily(uri, "./" + fontStr);
        }
        public static void SetControlFont(Control c, System.Windows.Media.FontFamily fam, double size, FontWeight w)
        {
            c.FontFamily = fam;
            c.FontSize = size;
            c.FontWeight = w;
            c.FontStretch = FontStretches.ExtraCondensed;
        }
        public static void SetControlFont(Control c, string fam, double size, FontWeight w)
        {
            SetControlFont(c, GetFontFromFile(fam), size, w);
        }

        // Common function which used by all elements as general code (initialization of element)
        public void WriteElementCommonCode(ref SquirrelCodeStack sq, Point positionOffset, string varName)
        {
            foreach (var iter in properties)
                switch (iter.Key)
                {
                    case "Position":
                        var pos = (Point)iter.Value;
                        sq.wFunc(null, varName, "setPosition",
                            strDoubleCorrect(pos.X + positionOffset.X) + ", " + strDoubleCorrect(pos.Y + positionOffset.Y) + ", false");
                        break;
                    case "Size":
                        var sz = (Point)iter.Value;
                        sq.wFunc(null, varName, "setSize",
                            strDoubleCorrect(sz.X) + ", " + strDoubleCorrect(sz.Y) + ", false");
                        break;
                    case "Text":
                        var txt = "\"" + iter.Value + "\"";
                        sq.wFunc(null, varName, "setText", txt);
                        break;
                    case "Visible":
                        sq.wFunc(null, varName, "setVisible", iter.Value.ToString().ToLower());
                        break;
                    
                    default:
                        if (iter.Value is CEGUI.GUIProperty)
                        {
                            var property = (CEGUI.GUIProperty)iter.Value;
                            sq.wFunc(null, varName, "setProperty", string.Format("\"{0}\", {1}", property.Name, property.ValueString));
                        }
                        break;
                }
        }
    }
}
